
#include <boost/lexical_cast.hpp>
#include <boost/timer/timer.hpp>

#include <arpa/inet.h>
#include <iostream>
#include <netinet/in.h>
#include <sys/socket.h>

#define BUFFER_SIZE 256
#define ITERS 200

int main(int argc, char const** argv)
{
  char buffer[256];

  // Arguments
  if (argc != 5)
  {
    std::cout << "ARGUMENTS: <recv port> <send address> <send port> <initiate=true|false>\n";
    return -1;
  }
  uint16_t const recv_port = boost::lexical_cast<uint16_t>(std::string(argv[1]));
  char const* send_address = argv[2];
  uint16_t const send_port = boost::lexical_cast<uint16_t>(std::string(argv[3]));
  bool initiate = std::string(argv[4]) == "true";

  // Receive socket
  int recv_socket = socket(PF_INET, SOCK_DGRAM, 0);
  if (recv_socket < 0) {
    std::cout << "error creating socket" << std::endl;
    return -1;
  }
  struct sockaddr_in recv_sock_addr;
  memset(&recv_sock_addr, 0, sizeof(struct sockaddr_in));
  recv_sock_addr.sin_family = AF_INET;
  recv_sock_addr.sin_port = recv_port;
  bind(recv_socket, (struct sockaddr*)&recv_sock_addr, sizeof(struct sockaddr_in));

  // Send socket
  int send_socket = socket(PF_INET, SOCK_DGRAM, 0);
  struct sockaddr_in send_sock_addr;
  memset(&send_sock_addr, 0, sizeof(struct sockaddr_in));
  send_sock_addr.sin_family = AF_INET;
  send_sock_addr.sin_port = send_port;
  if (inet_pton(PF_INET, send_address, &send_sock_addr.sin_addr) <= 0) {
    std::cout << "inet_pton failed" << std::endl;
    return -1;
  }

  if (initiate) {
    double nanos(0);
    double send_nanos(0);
    boost::timer::cpu_timer timer;
    for(unsigned i = 0; i != ITERS; ++i)
    {
      timer.start();
      sendto(send_socket, "ping", 4, 0, (struct sockaddr*)&send_sock_addr, sizeof(struct sockaddr_in));
      send_nanos += timer.elapsed().wall;
      recvfrom(recv_socket, buffer, BUFFER_SIZE, 0, NULL, NULL);
      nanos += timer.elapsed().wall;
    }
    std::cout << "Avg send: " << (send_nanos / ITERS) << " ns\n";
    std::cout << "Avg round trip: " << (nanos / ITERS) << " ns\n";
  } else {
    for(unsigned i = 0; i != ITERS; ++i)
    {
      recvfrom(recv_socket, buffer, BUFFER_SIZE, 0, NULL, NULL);
      sendto(send_socket, "ping", 4, 0, (struct sockaddr*)&send_sock_addr, sizeof(struct sockaddr_in));
    }
  }

  return 0;
}

